home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Ham Radio 2000 #2
/
Ham Radio 2000 - Volume 2.iso
/
HAMV2
/
TCP_IP
/
TNOS230S
/
PKTDRVR.C
< prev
next >
Wrap
C/C++ Source or Header
|
1997-07-30
|
14KB
|
587 lines
/* Driver for FTP Software's packet driver interface. (PC specific code)
* Rewritten Feb 1996 for DPMI with DJGPP Phil Karn
*/
#include "global.h"
#ifdef PACKET
#include <sys/types.h>
#include <go32.h>
#include <dos.h>
#include <dpmi.h>
#include "proc.h"
#include "netuser.h"
#include "enet.h"
#include "trace.h"
#include "pktdrvr.h"
#include "devparam.h"
#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: pktdrvr.c,v 1.16 1997/07/31 00:44:20 root Exp root $";
#endif
#define C_FLAG 1
static long access_type(int intno,int if_class,int if_type,
int if_number, uint rm_segment,uint rm_offset );
static int driver_info(int intno,int handle,int *version,
int *class,int *type,int *number,int *basic);
static int release_type(int intno,int handle);
static int get_address(int intno,int handle,uint8 *buf,int len);
#if 0
static int set_rcv_mode(int intno,int handle,int mode);
#endif
static int pk_raw(struct iface *iface,struct mbuf *bp);
static int pk_stop(struct iface *iface);
static void pkint(_go32_dpmi_registers *reg);
static struct pktdrvr Pktdrvr[PK_MAX];
static int Derr;
static void pk_rx(int dev,void *p1,void *p2);
/*
* Send routine for packet driver
*/
int
pk_send(
struct mbuf *bp, /* Buffer to send */
struct iface *iface, /* Pointer to interface control block */
uint32 gateway OPTIONAL, /* Ignored */
int prec OPTIONAL,
int del OPTIONAL,
int tput OPTIONAL,
int rel OPTIONAL
){
if (iface == NULL) {
free_p (bp);
return -1;
}
return (*iface->raw) (iface, bp);
}
/* Send raw packet (caller provides header) */
static int
pk_raw(
struct iface *iface, /* Pointer to interface control block */
struct mbuf *bp /* Data field */
){
_go32_dpmi_registers reg;
struct pktdrvr *pp;
int16 size;
int offset;
struct mbuf *bp1;
pp = &Pktdrvr[iface->dev];
size = len_p (bp);
dump (iface, IF_TRACE_OUT, pp->class, bp);
iface->rawsndcnt++;
iface->lastsent = secclock();
/* Perform class-specific processing, if any */
switch (pp->class){
case CL_ETHERNET:
if (size < RUNT) {
/* Pad the packet out to the minimum */
/* Do it securely with zeros */
if ((bp1 = alloc_mbuf (RUNT-size)) == NULLBUF) {
free_p (bp);
return -1;
}
memset (bp1->data, 0, RUNT-size);
bp1->cnt = RUNT - size;
append (&bp, bp1);
size = RUNT;
}
break;
case CL_KISS:
/* This *really* shouldn't be done here, but it was the
* easiest way. Put the type field for KISS TNC on front.
*/
if ((bp1 = pushdown (bp, 1)) == NULLBUF) {
free_p (bp);
return -1;
}
bp = bp1;
bp->data[0] = PARAM_DATA;
size++;
break;
default:
break;
}
/* Copy packet to contiguous real mode buffer */
offset = 0;
while(bp != NULL){
dosmemput(bp->data, bp->cnt, _go32_info_block.linear_address_of_transfer_buffer+offset);
offset += bp->cnt;
bp = free_mbuf (bp);
}
/* Call the packet driver to send it */
memset (®, 0, sizeof(reg));
reg.h.ah = SEND_PKT;
reg.x.si = _go32_info_block.linear_address_of_transfer_buffer & 15;
reg.x.ds = _go32_info_block.linear_address_of_transfer_buffer >> 4;
reg.x.cx = size;
(void) _go32_dpmi_simulate_int (pp->intno, ®);
if (reg.x.flags & C_FLAG){
Derr = reg.h.dh;
return -1;
}
return 0;
}
/* Packet driver receive upcall routine. Called by the packet driver TSR
* twice for each incoming packet: first with ax == 0 to allocate a buffer,
* and then with ax == 1 to signal completion of the copy.
*/
static void
pkint(_go32_dpmi_registers *reg)
{
int i;
uint len,blen;
int i_state;
struct pktdrvr *pp;
i_state = disable();
/* This is not really legal, since handles are not guaranteed to
* be globally unique. But it's extremely expedient.
*/
for (i = 0; i < PK_MAX; i++) {
if (Pktdrvr[i].handle == reg->x.bx)
break;
}
if (i == PK_MAX) {
restore (i_state);
reg->x.es = reg->x.di = 0;
return; /* Unknown handle */
}
pp = &Pktdrvr[i];
len = reg->x.cx;
switch (reg->x.ax){
case 0: /* Space allocate call */
if ((int32) (len + sizeof(len)) > pp->dossize - pp->cnt) {
/* Buffer overflow */
reg->x.es = 0;
reg->x.di = 0;
pp->overflows++;
break;
}
if ((int32) (pp->wptr + len + sizeof(len)) > pp->dossize) {
/* Not enough room at end of DOS buffer for length
* plus data, so write zero length and wrap around
*/
uint zero = 0;
pp->cnt += pp->dossize - pp->wptr;
dosmemput (&zero, sizeof(zero), pp->dosbase + pp->wptr);
pp->wptr = 0;
}
/* Write length into DOS buffer */
dosmemput (&len, sizeof(len), pp->dosbase + pp->wptr);
pp->wptr += (long) sizeof(len);
pp->cnt += (long) sizeof(len);
/* Pass new pointer to packet driver */
reg->x.es = (pp->dosbase + pp->wptr) / 16;
reg->x.di = (pp->dosbase + pp->wptr) % 16;
break;
case 1: /* Packet complete call */
/* blen is len rounded up to next boundary, to keep the
* next packet on a clean boundary
*/
blen = (len + sizeof(len) - 1) & ~(sizeof(len) - 1);
pp->wptr += (long) blen;
pp->cnt += (long) blen;
if ((int32) (pp->wptr + sizeof(len)) > pp->dossize) {
/* No room left for another len field, wrap */
pp->cnt += pp->dossize - pp->wptr;
pp->wptr = 0;
}
ksignal (&pp->cnt, 1);
break;
default:
break;
}
restore (i_state);
}
/* Shut down the packet interface */
static int
pk_stop(
struct iface *iface
){
struct pktdrvr *pp;
_go32_dpmi_seginfo dosmem;
pp = &Pktdrvr[iface->dev];
/* Call driver's release_type() entry */
if (release_type (pp->intno, pp->handle) == -1)
printf ("%s: release_type error code %u\n", iface->name, Derr);
pp->iface = NULL;
dosmem.size = pp->dossize / 16;
dosmem.rm_segment = pp->dosbase / 16;
(void) _go32_dpmi_free_dos_memory (&dosmem);
(void) _go32_dpmi_free_real_mode_callback (&pp->rmcb_seginfo);
return 0;
}
/* Attach a packet driver to the system
* argv[0]: hardware type, must be "packet"
* argv[1]: software interrupt vector, e.g., x7e
* argv[2]: interface label, e.g., "trw0"
* argv[3]: receive buffer size in kb
* argv[4]: maximum transmission unit, bytes, e.g., "1500"
* argv[5]: IP address (optional)
*/
int
pk_attach(
int argc,
char *argv[],
void *p
){
struct iface *if_pk;
int class,type;
char sig[9];
unsigned int intno;
int i;
struct pktdrvr *pp;
char tmp[25];
char *cp;
unsigned long pkt_addr;
unsigned short vec[2];
_go32_dpmi_seginfo dosmem;
#ifdef ARCNET
static uint8 arcip[] = {ARC_IP};
static uint8 arcarp[] = {ARC_ARP};
#endif
for (i = 0; i < PK_MAX; i++) {
if (Pktdrvr[i].iface == NULL)
break;
}
if (i >= PK_MAX) {
printf ("Too many packet drivers\n");
return -1;
}
if (if_lookup (argv[2]) != NULL){
printf ("Interface %s already exists\n", argv[2]);
return -1;
}
intno = htoi (argv[1]);
/* Verify that there's really a packet driver there, so we don't
* go off into the ozone (if there's any left)
*/
dosmemget (intno * 4, 4, vec);
pkt_addr = vec[1] * 16 + vec[0];
if (pkt_addr == 0) {
printf ("No packet driver loaded at int 0x%x\n", intno);
return -1;
}
dosmemget (pkt_addr + 3, 9, sig);
if (strcmp (sig, "PKT DRVR") != 0) {
printf ("No packet driver loaded at int 0x%x\n", intno);
return -1;
}
/* Find out what we've got */
if (driver_info ((int) intno, -1, NULL, &class, &type, NULL, NULL) < 0) {
printf ("driver_info call failed\n");
return -1;
}
pp = &Pktdrvr[i];
dosmem.size = 64 * atoi(argv[3]); /* KB -> paragraphs */
if (_go32_dpmi_allocate_dos_memory (&dosmem)) {
printf ("DOS memory allocate failed for %d, max size = %d\n", atoi(argv[3]) * 1024, dosmem.size * 16);
return -1;
}
pp->dossize = (long) (dosmem.size * 16);
pp->dosbase = dosmem.rm_segment * 16;
pp->overflows = pp->wptr = pp->rptr = pp->cnt = 0;
if_pk = (struct iface *) callocw (1, sizeof(struct iface));
if_pk->name = strdup(argv[2]);
if (argc > 5)
if_pk->addr = resolve (argv[5]);
else
if_pk->addr = Ip_addr;
if_pk->mtu = atoi (argv[4]);
if_pk->dev = i;
if_pk->raw = pk_raw;
if_pk->stop = pk_stop;
pp->intno = (int) intno;
pp->iface = if_pk;
pp->rmcb_seginfo.pm_offset = (int) pkint;
if ((i = _go32_dpmi_allocate_real_mode_callback_retf (&pp->rmcb_seginfo,
&pp->rmcb_registers)) != 0) {
printf ("real mode callback alloc failed: %d\n", i);
return -1;
}
pp->handle = access_type ((int) intno, class, ANYTYPE, 0, pp->rmcb_seginfo.rm_segment,
pp->rmcb_seginfo.rm_offset);
switch (class) {
case CL_ETHERNET:
(void) setencap(if_pk,"Ethernet");
/**** temp set multicast flag ****/
#if 0
i = set_rcv_mode (intno, pp->handle, 5);
printf ("set_rcv_mode returns %d, Derr = %d\n", i, Derr);
#endif
/* Get hardware Ethernet address from driver */
if_pk->hwaddr = mallocw (EADDR_LEN);
(void) get_address ((int) intno, pp->handle, (unsigned char *) if_pk->hwaddr, EADDR_LEN);
if (if_pk->hwaddr[0] & 1) {
printf ("Warning! Interface '%s' has a multicast address:", if_pk->name);
printf (" (%s)\n", (*if_pk->iftype->format)(tmp,if_pk->hwaddr));
}
break;
#ifdef ARCNET
case CL_ARCNET:
if_pk->send = anet_send;
if_pk->output = anet_output;
/* Get hardware ARCnet address from driver */
if_pk->hwaddr = mallocw (AADDR_LEN);
get_address (intno, pp->handle, if_pk->hwaddr, AADDR_LEN);
break;
#endif
case CL_SERIAL_LINE:
(void) setencap (if_pk, "SLIP");
break;
#ifdef AX25
case CL_KISS: /* Note that the raw routine puts on the command */
case CL_AX25:
(void) setencap (if_pk, "AX25");
if_pk->hwaddr = mallocw (AXALEN);
memcpy (if_pk->hwaddr, Mycall, AXALEN);
break;
#endif
case CL_SLFP:
(void) setencap (if_pk, "SLFP");
if_pk->send = pk_send;
(void) get_address ((int) intno, pp->handle, (uint8 *) &if_pk->addr, 4);
break;
default:
printf ("Packet driver has unsupported class %u\n", class);
free (if_pk->name);
free (if_pk);
return -1;
}
pp->class = class;
if_pk->next = Ifaces;
Ifaces = if_pk;
cp = if_name (if_pk, " rx");
if_pk->rxproc = newproc (cp, 768, pk_rx, if_pk->dev, if_pk, pp, 0);
free (cp);
return 0;
}
static long
access_type(
int intno,
int if_class,
int if_type,
int if_number,
uint rm_segment,
uint rm_offset
){
_go32_dpmi_registers reg;
memset (®, 0, sizeof(reg));
reg.h.ah = ACCESS_TYPE; /* Access_type() function */
reg.h.al = if_class; /* Class */
reg.x.bx = if_type; /* Type */
reg.h.dl = if_number; /* Number */
reg.x.es = rm_segment; /* Address of rm receive handler */
reg.x.di = rm_offset;
(void) _go32_dpmi_simulate_int (intno, ®);
if (reg.x.flags & C_FLAG) {
Derr = reg.h.dh;
return -1;
} else
return reg.x.ax;
}
static int
release_type(
int intno,
int handle
){
_go32_dpmi_registers reg;
memset (®, 0, sizeof(reg));
reg.x.bx = handle;
reg.h.ah = RELEASE_TYPE;
(void) _go32_dpmi_simulate_int (intno, ®);
if (reg.x.flags & C_FLAG) {
Derr = reg.h.dh;
return -1;
} else
return 0;
}
static int
driver_info(
int intno,
int handle,
int *version,
int *class,
int *type,
int *number,
int *basic
){
_go32_dpmi_registers reg;
memset (®, 0, sizeof(reg));
reg.h.ah = DRIVER_INFO;
reg.x.bx = handle;
reg.h.al = 0xff;
(void) _go32_dpmi_simulate_int (intno, ®);
if (reg.x.flags & C_FLAG) {
Derr = reg.h.dh;
return -1;
}
if(version != NULL)
*version = reg.x.bx;
if(class != NULL)
*class = reg.h.ch;
if(type != NULL)
*type = reg.x.dx;
if(number != NULL)
*number = reg.h.cl;
if(basic != NULL)
*basic = reg.h.al;
return 0;
}
static int
get_address(
int intno,
int handle,
uint8 *buf,
int len
){
_go32_dpmi_registers reg;
memset (®, 0, sizeof(reg));
reg.h.ah = GET_ADDRESS;
reg.x.bx = handle;
reg.x.di = _go32_info_block.linear_address_of_transfer_buffer & 15;
reg.x.es = _go32_info_block.linear_address_of_transfer_buffer >> 4;
reg.x.cx = len;
(void) _go32_dpmi_simulate_int (intno, ®);
if (reg.x.flags & C_FLAG) {
Derr = reg.h.dh;
return -1;
}
dosmemget (_go32_info_block.linear_address_of_transfer_buffer, len, buf);
return 0;
}
#if 0
static int
set_rcv_mode(
int intno,
int handle,
int mode
){
_go32_dpmi_registers reg;
memset (®, 0, sizeof(reg));
reg.h.ah = SET_RCV_MODE;
reg.x.cx = mode;
reg.x.bx = handle;
(void) _go32_dpmi_simulate_int (intno, ®);
if (reg.x.flags & C_FLAG) {
Derr = reg.h.dh;
return -1;
}
return 0;
}
#endif
static void
pk_rx(
int dev,
void *p1,
void *p2
) {
struct iface *iface = (struct iface *) p1;
struct pktdrvr *pp = (struct pktdrvr *) p2;
uint len,blen; /* len type must match size field in pkint */
struct mbuf *bp;
int cadj;
loop:
#if 0
while ((void) disable(), i = (volatile int) pp->cnt, (void) enable(), i == 0)
#else
while (pp->cnt == 0)
#endif
kwait (&pp->cnt);
cadj = 0;
/* Extract size */
dosmemget (pp->dosbase + pp->rptr, sizeof(len), &len);
if (len == 0) {
/* Writer wrapped around */
cadj += pp->dossize - pp->rptr;
pp->rptr = 0;
dosmemget (pp->dosbase, sizeof(len), &len);
}
/* Copy the packet into an mbuf and queue for the router */
bp = ambufw (len + sizeof(struct iface *));
bp->data += sizeof(struct iface *);
#ifdef debug
printf ("overf %d cnt %d start %d len %d\n", pp->overflows,
pp->cnt, pp->rptr + sizeof(len), len);
#endif
dosmemget (pp->dosbase + pp->rptr + sizeof(len), len, bp->data);
bp->cnt = len;
(void) net_route (iface, pp->class, bp);
/* figure length rounded up to next boundary */
blen = sizeof(len) + (len + sizeof(len) - 1) & ~(sizeof(len)-1);
cadj += (int) blen;
pp->rptr += (long) blen;
if ((int32) (pp->rptr + sizeof(len)) > pp->dossize) {
cadj += pp->dossize - pp->rptr;
pp->rptr = 0;
}
(void) disable();
pp->cnt -= cadj;
(void) enable();
goto loop;
}
#endif /* PACKET */